license: “CC BY-NC”
Creative Commons: Attribution-NonCommerical
https://creativecommons.org/licenses/by-nc/4.0/

Load library packages

library(tidyverse)
library(rvest)
library(htmltools)
tagList(rmarkdown::html_dependency_font_awesome())

Example

Import data

The following procedural example is based on documentation found at the rvest documentation site.

Import a webpage using read_html

results <- read_html("http://www.vondel.humanities.uva.nl/ecartico/persons/index.php?subtask=browse")

Results are in a list.

The results object is a list (R data type.) The items in the list correspond to the basic document structure of an HTML document…

Displaying the results object shows that the first item in the list is head. The second item is body. These items correspond to the basic structure of the HTML document type definition. In other words, the text, links, and HTML “stuff” were scraped from the web page. Specifically this stuff is found in the body element of the HTML document. This stuff is now stored in the body element of the restults list.

Contents of the results object

results

Example HTML

A simplified example HTML document

<HTML>
  <HEAD>
    <title>my example document</title>
  </HEAD>
  <BODY>
    <h1>Hello World</h1>
    <p>HTML is a tagging system known as the HypterText Markup Language</p>
  </BODY>
</HTML>

Procedure

The basic workflow of web scraping is

  1. Development

    • Import raw HTML of a single target page (page detail, or “leaf”)
    • Parse the HTML of the test page to gather the data you want
    • In a web browser, manually browse and understand the site navigation of the scrape target (site navigation, or “branches”)
    • Parse the site navigation and develop an interation plan
    • Iterate: write code that implements iteration, i.e. automated page crawling
    • Perform a dry run with a limited subset of the target web site
    • check robots.txt, terms of use, and construct time pauses (to avoid DNS attacks)
  2. Production

    • Iterate

      1. Crawl the site navigation (branches)
      2. Parse HTML for each detail page (leaves)

Parse the nodes of the HTML tree.

A web page is composed of HTML and prose. The web document, just as the web site, has a hierarchical structure. Web scraping means parsing these structures to gather needed information.

The first step is to start with a single target single document (i.e. a web page or a leaf of the site). In this case, the document we want to parse is the summary navigation page consisting of the first 50 names listed alphabetically in this web site. The goal is to parse the HTML source of that web page (i.e. document) by traversing the nodes of the document’s HTML structure. In other words, we want to mine text and data from the body section of the results list. In this example I’ll gather all the HTML within the <li> tags.

li stands for “list item”. You can learn more about the li tag structure from HTML documentation.

Goal: Briefly…

  • limit the results to the list item nodes of the body of the HTML document tree. This is done with the html_nodes() function.html_nodes("li")
  • Use the html_text() function to parse the text of the HTML list item (i.e. the <li> tag)

For Example

in an HTML document that has tagging such as this:

<li><a href="/ecartico/persons/17296">Anna Aaltse (1715 - 1738)</a></li>

I want to gather the text within the <li> tag: Anna Aaltse (1715 - 1738)

CODE

Using the html_nodes() and html_text() functions, I can retrieve all the text within <li></li> tags.

names <- results %>% 
  html_nodes("li") %>% 
  html_text()

names
 [1] "About ECARTICO"                                                 "+ Analysis"                                                    
 [3] "+ Search & browse"                                              "+ Impressum"                                                   
 [5] "Hillebrand Boudewynsz. van der Aa (1661 - 1717)"                "Boudewijn Pietersz van der Aa (? - ?)"                         
 [7] "Pieter Boudewijnsz. van der Aa (1659 - 1733)"                   "Boudewyn  van der Aa (1672 - ?)"                               
 [9] "Machtelt  van der Aa (? - ?)"                                   "Claas van der Aa I (? - ?)"                                    
[11] "Claas van der Aa II (? - ?)"                                    "Willem van der Aa (? - ?)"                                     
[13] "Hans  von Aachen, alias:  Hans / Johann van Aken (1552 - 1615)" "Jacobus van Aaken (? - ?)"                                     
[15] "Justus van Aaken (? - ?)"                                       "Johannes   Aalmis, alias:  Jan (1714 - 1799)"                  
[17] "Johan Bartholomeus   Aalmis (1723 - 1786)"                      "Maria  van Aalst (1639 - 1664)"                                
[19] "Anna   Aalst (? - ?)"                                           "Anna   Aaltse (1715 - 1738)"                                   
[21] "Allart   Aaltsz (1665 - 1748)"                                  "Geertruy   Aaltsz (? - 1732)"                                  
[23] "Maria   Aaltsz (? - 1746)"                                      "Catharina   Aaltsz (? - 1727)"                                 
[25] "Nikolaas  van Aaltwijk (1692 - 1727)"                           "Maria   Aams (1711 - 1774)"                                    
[27] "Jacobus   Aams (1680 - ?)"                                      "Jan Govertsz. van der Aar (1544 - 1612)"                       
[29] "Anna  van der Aar (1576 - 1656)"                                "Janneke Jans van Aarden (1609 - 1651)"                         
[31] "Abraham  van Aardenberg (1672 - 1717)"                          "Willem Aardenhout I (? - ?)"                                   
[33] "Margrietje   Aarlincx (1637 - 1690)"                            "Dirck  van Aart (1680 - 1737)"                                 
[35] "Jonas   Abarbanel, alias:  Abravanel; Abrabanel (? - 1667)"     "Josephus   Abarbanel (? - ?)"                                  
[37] "Esther    Abarbanel (? - ?)"                                    "Rachel   Abarbanel (? - ?)"                                    
[39] "Lea   Abarbanel (1691 - ?)"                                     "Isaac   Abarbanel (1637 - 1723)"                               
[41] "Damiana    Abarca (? - 1630)"                                   "Bartholomeus   Abba (1641 - 1684)"                             
[43] "Cornelis  Dirksz.   Abba (1604 - 1675)"                         "Clara   Abba (1631 - 1671)"                                    
[45] "Aerlant   Abbas (1606 - 1696)"                                  "Matheus Jansz  Abbas, alias:  Diercsz (1569 - ?)"              
[47] "Hendrik   Abbé, alias:  Enrico Abè (1639 - 1677)"               "Claude   Abbé, alias:  Glaude (? - 1653)"                      
[49] "Simon Jan Pontenz.  Abbe (1467 - 1549)"                         "Simon IJsbrandz.  Abbe (? - ?)"                                
[51] "Ysbrandt Simonsz.  Abbe (? - 1559)"                             "Maximiliaen  l' Abbé, alias:  Labbé (? - 1675)"                
[53] "Marten Simonsz.  Abbe genaamd Schuyt (? - 1592)"                "Daniël   Abbeloos (ca. 1635 - 1677)"                           

Parse the HTML attributes of an HTML tag…

Beyond the text you may also want attributes of HTML tags. To mine the URL of a hypertext link <a href="URL"></a>, within a list item, you need to parse the HREF argument of an anchor tag. If you’re new to web scraping, you’re going to need to learn something about HTML tags, such as the anchor tag.

For Example

in an HTML document that has tagging such as this:

<a href="https://search.com">Example Link</a>

I want to gather the value of the href attribute within the anchor tag: https://search.com

CODE

Using the html_nodes() and html_attr() functions, I can retrieve all the attribute values within <li><a></a></li> tags.

url <- results %>% 
  html_nodes("li a") %>% 
  html_attr("href")

url
 [1] "../"                     "../analysis/"            "../persons/"             "../impressum/"           "/ecartico/persons/414"  
 [6] "/ecartico/persons/10566" "/ecartico/persons/10567" "/ecartico/persons/10568" "/ecartico/persons/27132" "/ecartico/persons/33780"
[11] "/ecartico/persons/33781" "/ecartico/persons/33782" "/ecartico/persons/9203"  "/ecartico/persons/33052" "/ecartico/persons/33053"
[16] "/ecartico/persons/43671" "/ecartico/persons/43672" "/ecartico/persons/30222" "/ecartico/persons/38845" "/ecartico/persons/17296"
[21] "/ecartico/persons/38518" "/ecartico/persons/38523" "/ecartico/persons/38524" "/ecartico/persons/38525" "/ecartico/persons/43337"
[26] "/ecartico/persons/42619" "/ecartico/persons/42620" "/ecartico/persons/41311" "/ecartico/persons/49902" "/ecartico/persons/20653"
[31] "/ecartico/persons/47922" "/ecartico/persons/33783" "/ecartico/persons/42051" "/ecartico/persons/28921" "/ecartico/persons/37887"
[36] "/ecartico/persons/37890" "/ecartico/persons/37892" "/ecartico/persons/38352" "/ecartico/persons/42876" "/ecartico/persons/42881"
[41] "/ecartico/persons/22859" "/ecartico/persons/52962" "/ecartico/persons/52963" "/ecartico/persons/52965" "/ecartico/persons/17297"
[46] "/ecartico/persons/55241" "/ecartico/persons/416"   "/ecartico/persons/11593" "/ecartico/persons/41739" "/ecartico/persons/41742"
[51] "/ecartico/persons/41743" "/ecartico/persons/52649" "/ecartico/persons/41738" "/ecartico/persons/417"  

Note that the above links, or hrefs, are relative URL paths. I still need the domain name for the web server http://www.vondel.humanities.uva.nl.

Systematize

Above I created two vectors, one vector, names, is the html_text that I parsed from the <li> tags within the <body> of the HTML document. The other vector, url, is a vector of the values of the href attribute of the anchor <a> tags.

Placing both vectors into a tibble makes manipulation easier when using tidyverse techniques.

Goal

I want to develop a systematic workflow that builds a tibble consisting of each of the 50 names listed in the summary results object retrieved by read_html() performed on this page. Of course, mining and parsing the data is just the beginning. Data cleaning is a vital and constant aspect of web scraping.

Build Tibble

Using vectors from parsing functions above….

results_df <- tibble(names, url)

results_df

From above we have links in a url vector, and target names in a names vector, for fifty names from the target website we want to crawl. Of course you also want to parse data for each person in the database. To do this we need to read (i.e. read_html()) the HTML for each relevant url in the results_df tibble. Below is an example of how to systematize the workflow. To do that, we’ll make a results tibble, results_df. But first, more data cleaning…

Create some new variables with mutate. Build a full URL from the relative URL path (i.e. the url vector) and the domain or base URL of the target site. Since we scraped the relative URL path, we have to construct a full URL.

urls_to_crawl_df <- results_df %>% 
  # mutate(base_url = "") %>% 
  mutate(full_url = glue::glue("http://www.vondel.humanities.uva.nl{url}")) %>% 
  mutate(full_url = str_replace_all(full_url, "\\.\\.", "")) %>% 
  select(full_url)

urls_to_crawl_df  # 

As you can see, above, it’s really helpful to know about Tidyverse text manipulation, specifically mutate, glue, and pattern matching and regex using the stringr package.

Operationalize the workflow

To operationalize this part of the workflow, you want to iterate over the vector full_url found in the urls_to_crawl_df tibble. Then read_html for each name that interest you. Remember that only 50 of the 54 rows in the resutls_df tibble are target names to crawl. So, really, you still have some data wrangling to do. How can you eliminate the four rows in the results_df tibble that are not targets (i.e. names)? Somewhere, below, I’ll also show you how to exclude the four rows of unnecessary/unhelpful information.

Iterate

Use purrr::map instead of ‘for’ loops. Because purrr is the R/Tidyverse way. ‘For’ loops are fine, but invest some time learning purrr and you’ll be better off. Still, there’s no wrong way to iterate as long as you get the right answer. So, do what works. Below is the Tidyverse/Purrr way….

Now that I have a full list of navigation URLs, each of which represents a web page that has a summary of 50 names/links. My next task is to read the HTML of each URL representing a target-name. By reading the URL (importing the HTML) for each target name, I will then have HTML for each individual target person in the database. Of course, I still, then, have to read and parse the HTML of those target-name pages, but I can do that. The scraping (crawling + parsing) works when I have a URL per target person. Having a URL for each target person means I can systematically scrape the web site. In other words, I crawl the summary navigation to construct a URL for each summary page. Then I import HTML for each summary page to get a URL to each person’s page. Then I import each person’s page and parse the HTML for each person’s record.

But, back to the current task: import the HTML for each summary results page of 50 records…

Note: that, below, I introduce a pause (Sys.sleep()) in front of each read_html() function. This is a common technique for well behaved web scraping. Pausing before each read_html function, avoids overwhelming my target’s server/network infrastructure. If I overwhelm the target server, the server host-people may consider me a DNS attack. If they think I’m a DNS attacker, they might choose to block my computer from crawling their site. If that happens, I’m up a creek. I don’t want that. I want my script to be a well behaved bot-crawler.

Speaking of being a good and honorable scraper-citizen, did I browse the robots.txt page for the site? Did I check the site for a Terms of Service page? Did I look to see if there were any written prohibitions against web crawling, systematic downloading, copyright, or licensing restrictions? I did and you should too. As of this writing, there do not appear to be any restrictions for this site. You should perform these types of good-scraping hygiene steps for every site you want to scrape!

Note: Below, for development purposes, I limited my crawling to 3 results links: my_url_df$url[1:3]. Be conservative during your code development to avoid appearing as a DNS attacker. Later, when you are ready to crawl your whole target site, you’ll want to remove such limits (i.e. [1:3].) But for now, do everyone a favor and try not to be over confident. Stay in the kiddie pool. Do your development work until you are sure you’re not accidentally unleashing a malicious or poorly constructed web crawler.

Note: Below, I am keeping the original target URL variable, summary_url, for later reference. This way I will have a record of which parsed data results came from which URL web page.

Note: Below, the final result is a tibble with a vector, summary_url, and an associated column of HTML results, each result is stored as a nested R list. That is, a column of data types that are all “lists”, aka a “list column”. Personally I find lists to be a pain. I prefer working with tibbles (aka data frames.). But lists appear often in R data wrangling, especially when scraping with rvest. The more you work with lists, the more you come to tolerate lists for the flexible data type that they are. Anyway, if I were to look at only the first row of results from the html_results column, nav_results_list$html_results[1], I would find a list of the raw HTML from the first summary results page imported via read_html().

Recapping: This is testing. I have three URLs (html_reults[1:3]), one for each of the first three navigation summary pages. Each summary page will contain the raw HTML for 50 names. I will read_html each link, waiting 2 seconds between each read_html.

nav_results_list <- tibble(
  html_results = map(nav_df$url[1:3],
    ~ {
      #url[1:3] - limiting to the first three summary results pages (each page = 50 results)
      Sys.sleep(2)
      # DO THIS!  sleep 2 will pause 2 seconds between server requests to avoid being identified and potentially blocked by my target web server that might see my crawling bot as a DNS attack.
      .x %>%
        read_html()
    }),
  summary_url = nav_df$url[1:3]
)

nav_results_list

Now I have three rows of lists, each list with 50 links, in a tibble. Each link leads to a target name that I can eventually read_html to gather the raw HTML of that target name.

But first, I want to expand the three lists so I have a single tibble of 150 URLs to target names. Using purrr (map()), I can iterate over the results lists, parsing the HTML nodes with html_attr() and html_text(). It is convenient to keep this parsed data in a tibble. The results will be nested lists within a tibble. When I expand the nested list with the unnest() function, I then have a single tibble with 150 URLs and 150 names, one row for each target name.

results_by_page <- tibble(summary_url = nav_results_list$summary_url, 
                          url =
                            map(nav_results_list$html_results,
                                ~ .x %>%
                                  html_nodes("ul li a") %>%
                                  html_attr("href")),
                          name =
                            map(nav_results_list$html_results,
                                ~ .x %>%
                                  html_nodes("ul li a") %>%
                                  html_text()
                                )
                          )

results_by_page

results_by_page %>% 
  unnest(cols = c(url, name)) %>% 
  filter(!str_detect(name, "ECARTICO")) %>% 
  filter(!str_detect(name, "^\\+"))
NA

Now I can iterate over each one of the URLs to the target names. Then I can parse the raw HTML for each target name page. When I follow the links for each name, I have the raw HTML of each person, in lists, ready to be parsed with the html_nodes, html_text, and html_attr functions.

Parsing example for an individual

Now you know how to get a URL for each name in the target database. That is, you can crawl the target site’s navigation. The next goal is to import and parse the HTML for each name. In other words, in my development tibble, I still need to crawl the individual target names, all 150 names, 50 names per summary page for each of the 3 development pages. Below is an example of gathering and parsing information for one URL representing one person. The information gathered is information from the detailed names page about the children of one person in the target database.

# http://www.vondel.humanities.uva.nl/ecartico/persons/10579
# schema:children

emanuel <-  read_html("http://www.vondel.humanities.uva.nl/ecartico/persons/10579")

child_filter <- emanuel %>% 
  html_nodes("ul li a") %>% 
  html_attr("rel") %>% 
  as_tibble() %>% 
  mutate(id = row_number()) %>% 
  filter(str_detect(value, "children"))
child_filter

child_text <- emanuel  %>% 
  html_nodes("ul li a") %>% 
  html_text() %>% 
  as_tibble() %>% 
  rename(text = value) %>% 
  mutate(id = row_number()) %>% 
  inner_join(child_filter)

child_text %>% 
  pull(text)

Don’t forget to use a pause Sys.sleep() between each systematic iteration of the read_html() function.

Resources


John Little

Data Science Librarian
Center for Data & Visualization Sciences
Duke University Libraries

https://JohnLittle.info
https://Rfun.library.duke.edu
https://library.duke.edu/data

 
Creative Commons: Attribution-NonCommercial 4.0
https://creativecommons.org/licenses/by-nc/4.0

 

LS0tDQp0aXRsZTogInJ2ZXN0IHR1dG9yaWFsIGRlbW9uc3RyYXRpb24iDQphdXRob3I6ICJKb2huIExpdHRsZSINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCmxpY2Vuc2U6ICJDQyBCWS1OQyIgIA0KQ3JlYXRpdmUgQ29tbW9uczogIEF0dHJpYnV0aW9uLU5vbkNvbW1lcmljYWwgIA0KaHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LW5jLzQuMC8gIA0KDQoNCiMjIExvYWQgbGlicmFyeSBwYWNrYWdlcw0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHJ2ZXN0KQ0KbGlicmFyeShodG1sdG9vbHMpDQp0YWdMaXN0KHJtYXJrZG93bjo6aHRtbF9kZXBlbmRlbmN5X2ZvbnRfYXdlc29tZSgpKQ0KYGBgDQoNCiMjIEV4YW1wbGUNCg0KIyMjIEltcG9ydCBkYXRhDQoNClRoZSBmb2xsb3dpbmcgcHJvY2VkdXJhbCBleGFtcGxlIGlzIGJhc2VkIG9uIGRvY3VtZW50YXRpb24gZm91bmQgYXQgdGhlIFtgcnZlc3RgXShodHRwczovL3J2ZXN0LnRpZHl2ZXJzZS5vcmcvKSBkb2N1bWVudGF0aW9uIHNpdGUuDQoNCiMjIyMgSW1wb3J0IGEgd2VicGFnZSB1c2luZyBgcmVhZF9odG1sYA0KDQpgYGB7cn0NCnJlc3VsdHMgPC0gcmVhZF9odG1sKCJodHRwOi8vd3d3LnZvbmRlbC5odW1hbml0aWVzLnV2YS5ubC9lY2FydGljby9wZXJzb25zL2luZGV4LnBocD9zdWJ0YXNrPWJyb3dzZSIpDQpgYGANCg0KIyMjIFJlc3VsdHMgYXJlIGluIGEgbGlzdC4NCg0KVGhlIGByZXN1bHRzYCBvYmplY3QgaXMgYSBfbGlzdF8gKFIgZGF0YSB0eXBlLikgIFRoZSBpdGVtcyBpbiB0aGUgbGlzdCBjb3JyZXNwb25kIHRvIHRoZSBiYXNpYyBkb2N1bWVudCBzdHJ1Y3R1cmUgb2YgYW4gSFRNTCBkb2N1bWVudC4uLg0KDQpEaXNwbGF5aW5nIHRoZSBgcmVzdWx0c2Agb2JqZWN0IHNob3dzIHRoYXQgdGhlIGZpcnN0IGl0ZW0gaW4gdGhlIF9saXN0XyBpcyBgaGVhZGAuICBUaGUgc2Vjb25kIGl0ZW0gaXMgYGJvZHlgLiAgVGhlc2UgaXRlbXMgY29ycmVzcG9uZCB0byB0aGUgYmFzaWMgc3RydWN0dXJlIG9mIHRoZSBIVE1MIGRvY3VtZW50IHR5cGUgZGVmaW5pdGlvbi4gIEluIG90aGVyIHdvcmRzLCB0aGUgX3RleHQsIGxpbmtzXywgYW5kIEhUTUwgInN0dWZmIiB3ZXJlIHNjcmFwZWQgZnJvbSB0aGUgd2ViIHBhZ2UuICBTcGVjaWZpY2FsbHkgdGhpcyBzdHVmZiBpcyBmb3VuZCBpbiB0aGUgX2JvZHlfIGVsZW1lbnQgb2YgdGhlIEhUTUwgZG9jdW1lbnQuIFRoaXMgc3R1ZmYgaXMgbm93IHN0b3JlZCBpbiB0aGUgYGJvZHlgIGVsZW1lbnQgb2YgdGhlIGByZXN0dWx0c2AgX2xpc3RfLg0KDQoqKkNvbnRlbnRzIG9mIHRoZSBgcmVzdWx0c2Agb2JqZWN0KioNCg0KYGBge3J9DQpyZXN1bHRzDQpgYGANCg0KKipFeGFtcGxlIEhUTUwqKg0KDQpBIHNpbXBsaWZpZWQgZXhhbXBsZSBIVE1MIGRvY3VtZW50DQoNCmBgYGh0bWwNCjxIVE1MPg0KICA8SEVBRD4NCiAgICA8dGl0bGU+bXkgZXhhbXBsZSBkb2N1bWVudDwvdGl0bGU+DQogIDwvSEVBRD4NCiAgPEJPRFk+DQogICAgPGgxPkhlbGxvIFdvcmxkPC9oMT4NCiAgICA8cD5IVE1MIGlzIGEgdGFnZ2luZyBzeXN0ZW0ga25vd24gYXMgdGhlIEh5cHRlclRleHQgTWFya3VwIExhbmd1YWdlPC9wPg0KICA8L0JPRFk+DQo8L0hUTUw+DQpgYGANCg0KDQoNCiMjIyBQcm9jZWR1cmUNCg0KVGhlIGJhc2ljIHdvcmtmbG93IG9mIHdlYiBzY3JhcGluZyBpcw0KDQoxLiBEZXZlbG9wbWVudA0KDQogICAgLSBJbXBvcnQgcmF3IEhUTUwgb2YgYSBzaW5nbGUgdGFyZ2V0IHBhZ2UgKHBhZ2UgZGV0YWlsLCBvciAibGVhZiIpDQogICAgLSBfUGFyc2VfIHRoZSBIVE1MIG9mIHRoZSB0ZXN0IHBhZ2UgdG8gZ2F0aGVyIHRoZSBkYXRhIHlvdSB3YW50DQogICAgLSBJbiBhIHdlYiBicm93c2VyLCBtYW51YWxseSBicm93c2UgYW5kIHVuZGVyc3RhbmQgdGhlIHNpdGUgbmF2aWdhdGlvbiBvZiB0aGUgc2NyYXBlIHRhcmdldCAoc2l0ZSBuYXZpZ2F0aW9uLCBvciAiYnJhbmNoZXMiKQ0KICAgIC0gX1BhcnNlXyB0aGUgc2l0ZSBuYXZpZ2F0aW9uIGFuZCBkZXZlbG9wIGFuIGludGVyYXRpb24gcGxhbg0KICAgIC0gSXRlcmF0ZTogIHdyaXRlIGNvZGUgdGhhdCBpbXBsZW1lbnRzIGl0ZXJhdGlvbiwgaS5lLiBhdXRvbWF0ZWQgcGFnZSBfY3Jhd2xpbmdfIA0KICAgIC0gUGVyZm9ybSBhIGRyeSBydW4gd2l0aCBhIGxpbWl0ZWQgc3Vic2V0IG9mIHRoZSB0YXJnZXQgd2ViIHNpdGUNCiAgICAtIGNoZWNrIHJvYm90cy50eHQsIHRlcm1zIG9mIHVzZSwgYW5kIGNvbnN0cnVjdCB0aW1lIHBhdXNlcyAodG8gYXZvaWQgRE5TIGF0dGFja3MpDQoNCjIuIFByb2R1Y3Rpb24NCg0KICAgIC0gSXRlcmF0ZQ0KICAgIA0KICAgICAgICBhLiAqKkNyYXdsKiogdGhlIHNpdGUgbmF2aWdhdGlvbiAoYnJhbmNoZXMpDQogICAgICAgIGIuICoqUGFyc2UqKiBIVE1MIGZvciBlYWNoIGRldGFpbCBwYWdlIChsZWF2ZXMpDQogICAgICAgIA0KIyMjIFBhcnNlIHRoZSBub2RlcyBvZiB0aGUgSFRNTCB0cmVlLiAgDQoNCkEgd2ViIHBhZ2UgaXMgY29tcG9zZWQgb2YgSFRNTCBhbmQgcHJvc2UuICBUaGUgd2ViIGRvY3VtZW50LCBqdXN0IGFzIHRoZSB3ZWIgc2l0ZSwgaGFzIGEgaGllcmFyY2hpY2FsIHN0cnVjdHVyZS4gIFdlYiBzY3JhcGluZyBtZWFucyBwYXJzaW5nIHRoZXNlIHN0cnVjdHVyZXMgdG8gZ2F0aGVyIG5lZWRlZCBpbmZvcm1hdGlvbi4NCg0KVGhlIGZpcnN0IHN0ZXAgaXMgdG8gc3RhcnQgd2l0aCBhIHNpbmdsZSB0YXJnZXQgc2luZ2xlIGRvY3VtZW50IChpLmUuIGEgd2ViIHBhZ2Ugb3IgYSBsZWFmIG9mIHRoZSBzaXRlKS4gIEluIHRoaXMgY2FzZSwgdGhlIGRvY3VtZW50IHdlIHdhbnQgdG8gcGFyc2UgaXMgdGhlIFtzdW1tYXJ5IG5hdmlnYXRpb24gcGFnZV0oaHR0cDovL3d3dy52b25kZWwuaHVtYW5pdGllcy51dmEubmwvZWNhcnRpY28vcGVyc29ucy9pbmRleC5waHA/c3VidGFzaz1icm93c2UpIGNvbnNpc3Rpbmcgb2YgdGhlIGZpcnN0IDUwIG5hbWVzIGxpc3RlZCBhbHBoYWJldGljYWxseSBpbiB0aGlzIHdlYiBzaXRlLiBUaGUgZ29hbCBpcyB0byBwYXJzZSB0aGUgSFRNTCBzb3VyY2Ugb2YgdGhhdCB3ZWIgcGFnZSAoaS5lLiBkb2N1bWVudCkgYnkgdHJhdmVyc2luZyB0aGUgbm9kZXMgb2YgdGhlIGRvY3VtZW50J3MgSFRNTCBzdHJ1Y3R1cmUuICBJbiBvdGhlciB3b3Jkcywgd2Ugd2FudCB0byBtaW5lIHRleHQgYW5kIGRhdGEgZnJvbSB0aGUgYGJvZHlgIHNlY3Rpb24gb2YgdGhlIGByZXN1bHRzYCBfbGlzdF8uICBJbiB0aGlzIGV4YW1wbGUgSSdsbCBnYXRoZXIgYWxsIHRoZSBIVE1MIHdpdGhpbiB0aGUgYDxsaT5gIHRhZ3MuICANCg0KYGxpYCBzdGFuZHMgZm9yICJsaXN0IGl0ZW0iLiAgWW91IGNhbiBsZWFybiBtb3JlIGFib3V0IHRoZSBfbGlfIHRhZyBzdHJ1Y3R1cmUgZnJvbSBbSFRNTCBkb2N1bWVudGF0aW9uXShodHRwczovL3d3dy53M3NjaG9vbHMuY29tL1RBR1MvdGFnX2xpLmFzcCkuDQoNCioqR29hbDogIEJyaWVmbHkuLi4qKg0KDQotIGxpbWl0IHRoZSByZXN1bHRzIHRvIHRoZSBfbGlzdCBpdGVtXyAqKm5vZGVzKiogb2YgdGhlIGBib2R5YCBvZiB0aGUgSFRNTCBkb2N1bWVudCB0cmVlLiAgVGhpcyBpcyBkb25lIHdpdGggdGhlIGBodG1sX25vZGVzKClgIGZ1bmN0aW9uLmBodG1sX25vZGVzKCJsaSIpYA0KLSBVc2UgdGhlIGBodG1sX3RleHQoKWAgZnVuY3Rpb24gdG8gcGFyc2UgdGhlIHRleHQgb2YgdGhlIEhUTUwgX2xpc3QgaXRlbV8gKGkuZS4gdGhlIGA8bGk+YCB0YWcpDQoNCioqRm9yIEV4YW1wbGUqKg0KDQppbiBhbiBIVE1MIGRvY3VtZW50IHRoYXQgaGFzIHRhZ2dpbmcgc3VjaCBhcyB0aGlzOg0KDQpgYGBodG1sDQo8bGk+PGEgaHJlZj0iL2VjYXJ0aWNvL3BlcnNvbnMvMTcyOTYiPkFubmEgQWFsdHNlICgxNzE1IC0gMTczOCk8L2E+PC9saT4NCmBgYA0KDQpJIHdhbnQgdG8gZ2F0aGVyIHRoZSB0ZXh0IHdpdGhpbiB0aGUgYDxsaT5gIHRhZzogICoqQW5uYSBBYWx0c2UgKDE3MTUgLSAxNzM4KSoqDQoNCioqQ09ERSoqDQoNClVzaW5nIHRoZSBgaHRtbF9ub2RlcygpYCBhbmQgYGh0bWxfdGV4dCgpYCBmdW5jdGlvbnMsIEkgY2FuIHJldHJpZXZlIGFsbCB0aGUgdGV4dCB3aXRoaW4gYDxsaT48L2xpPmAgdGFncy4NCg0KYGBge3J9DQpuYW1lcyA8LSByZXN1bHRzICU+JSANCiAgaHRtbF9ub2RlcygibGkiKSAlPiUgDQogIGh0bWxfdGV4dCgpDQoNCm5hbWVzDQpgYGANCg0KIyMjIFBhcnNlIHRoZSBIVE1MIGF0dHJpYnV0ZXMgb2YgYW4gSFRNTCB0YWcuLi4NCg0KQmV5b25kIHRoZSB0ZXh0IHlvdSBtYXkgYWxzbyB3YW50IGF0dHJpYnV0ZXMgb2YgSFRNTCB0YWdzLiAgVG8gbWluZSB0aGUgVVJMIG9mIGEgaHlwZXJ0ZXh0IGxpbmsgYDxhIGhyZWY9IlVSTCI+PC9hPmAsIHdpdGhpbiBhIGxpc3QgaXRlbSwgeW91IG5lZWQgdG8gcGFyc2UgdGhlIEhSRUYgYXJndW1lbnQgb2YgYW4gYW5jaG9yIHRhZy4gIElmIHlvdSdyZSBuZXcgdG8gd2ViIHNjcmFwaW5nLCB5b3UncmUgZ29pbmcgdG8gbmVlZCB0byBsZWFybiBzb21ldGhpbmcgYWJvdXQgSFRNTCB0YWdzLCBzdWNoIGFzIHRoZSBbYW5jaG9yIHRhZ10oaHR0cHM6Ly93d3cudzNzY2hvb2xzLmNvbS9UQUdTL3RhZ19hLmFzcCkuDQoNCioqRm9yIEV4YW1wbGUqKg0KDQppbiBhbiBIVE1MIGRvY3VtZW50IHRoYXQgaGFzIHRhZ2dpbmcgc3VjaCBhcyB0aGlzOg0KDQpgYGBodG1sDQo8YSBocmVmPSJodHRwczovL3NlYXJjaC5jb20iPkV4YW1wbGUgTGluazwvYT4NCmBgYCANCg0KSSB3YW50IHRvIGdhdGhlciB0aGUgdmFsdWUgb2YgdGhlIGBocmVmYCBhdHRyaWJ1dGUgd2l0aGluIHRoZSBhbmNob3IgdGFnOiAgKipodHRwczovL3NlYXJjaC5jb20qKg0KDQoqKkNPREUqKg0KDQpVc2luZyB0aGUgYGh0bWxfbm9kZXMoKWAgYW5kIGBodG1sX2F0dHIoKWAgZnVuY3Rpb25zLCBJIGNhbiByZXRyaWV2ZSBhbGwgdGhlIGF0dHJpYnV0ZSB2YWx1ZXMgd2l0aGluIGA8bGk+PGE+PC9hPjwvbGk+YCB0YWdzLg0KDQpgYGB7cn0NCnVybCA8LSByZXN1bHRzICU+JSANCiAgaHRtbF9ub2RlcygibGkgYSIpICU+JSANCiAgaHRtbF9hdHRyKCJocmVmIikNCg0KdXJsDQpgYGANCg0KTm90ZSB0aGF0IHRoZSBhYm92ZSBsaW5rcywgb3IgX2hyZWZzXywgYXJlIHJlbGF0aXZlIFVSTCBwYXRocy4gIEkgc3RpbGwgbmVlZCB0aGUgZG9tYWluIG5hbWUgZm9yIHRoZSB3ZWIgc2VydmVyIGBodHRwOi8vd3d3LnZvbmRlbC5odW1hbml0aWVzLnV2YS5ubGAuDQoNCiMjIFN5c3RlbWF0aXplDQoNCkFib3ZlIEkgY3JlYXRlZCB0d28gdmVjdG9ycywgb25lIHZlY3RvciwgYG5hbWVzYCwgaXMgdGhlIGBodG1sX3RleHRgIHRoYXQgSSBwYXJzZWQgZnJvbSB0aGUgYDxsaT5gIHRhZ3Mgd2l0aGluIHRoZSBgPGJvZHk+YCBvZiB0aGUgSFRNTCBkb2N1bWVudC4gIFRoZSBvdGhlciB2ZWN0b3IsIGB1cmxgLCBpcyBhIHZlY3RvciBvZiB0aGUgdmFsdWVzIG9mIHRoZSBgaHJlZmAgYXR0cmlidXRlIG9mIHRoZSBhbmNob3IgYDxhPmAgdGFncy4gDQoNClBsYWNpbmcgYm90aCB2ZWN0b3JzIGludG8gYSB0aWJibGUgbWFrZXMgbWFuaXB1bGF0aW9uIGVhc2llciB3aGVuIHVzaW5nIHRpZHl2ZXJzZSB0ZWNobmlxdWVzLg0KDQoqKkdvYWwqKg0KDQpJIHdhbnQgdG8gZGV2ZWxvcCBhIHN5c3RlbWF0aWMgd29ya2Zsb3cgdGhhdCBidWlsZHMgYSB0aWJibGUgY29uc2lzdGluZyBvZiBlYWNoIG9mIHRoZSA1MCBuYW1lcyBsaXN0ZWQgaW4gdGhlIHN1bW1hcnkgYHJlc3VsdHNgIG9iamVjdCByZXRyaWV2ZWQgYnkgYHJlYWRfaHRtbCgpYCBwZXJmb3JtZWQgb24gW3RoaXMgcGFnZV0oaHR0cDovL3d3dy52b25kZWwuaHVtYW5pdGllcy51dmEubmwvZWNhcnRpY28vcGVyc29ucy9pbmRleC5waHA/c3VidGFzaz1icm93c2UpLiAgT2YgY291cnNlLCBtaW5pbmcgYW5kIHBhcnNpbmcgdGhlIGRhdGEgaXMganVzdCB0aGUgYmVnaW5uaW5nLiAgRGF0YSBjbGVhbmluZyBpcyBhIHZpdGFsIGFuZCBjb25zdGFudCBhc3BlY3Qgb2Ygd2ViIHNjcmFwaW5nLg0KDQojIyMgQnVpbGQgVGliYmxlDQoNClVzaW5nIHZlY3RvcnMgZnJvbSBwYXJzaW5nIGZ1bmN0aW9ucyBhYm92ZS4uLi4NCg0KYGBge3J9DQpyZXN1bHRzX2RmIDwtIHRpYmJsZShuYW1lcywgdXJsKQ0KDQpyZXN1bHRzX2RmDQpgYGANCg0KRnJvbSBhYm92ZSB3ZSBoYXZlIGxpbmtzIGluIGEgYHVybGAgdmVjdG9yLCBhbmQgdGFyZ2V0IG5hbWVzIGluIGEgYG5hbWVzYCB2ZWN0b3IsIGZvciBmaWZ0eSBuYW1lcyBmcm9tIHRoZSB0YXJnZXQgd2Vic2l0ZSB3ZSB3YW50IHRvIGNyYXdsLiAgT2YgY291cnNlIHlvdSBhbHNvIHdhbnQgdG8gcGFyc2UgZGF0YSBmb3IgIGVhY2ggcGVyc29uIGluIHRoZSBkYXRhYmFzZS4gIFRvIGRvIHRoaXMgd2UgbmVlZCB0byByZWFkIChpLmUuIGByZWFkX2h0bWwoKWApIHRoZSBIVE1MIGZvciBlYWNoICByZWxldmFudCBgdXJsYCBpbiB0aGUgcmVzdWx0c19kZiB0aWJibGUuICBCZWxvdyBpcyBhbiBleGFtcGxlIG9mIGhvdyB0byBzeXN0ZW1hdGl6ZSB0aGUgd29ya2Zsb3cuIFRvIGRvIHRoYXQsIHdlJ2xsIG1ha2UgYSByZXN1bHRzIHRpYmJsZSwgYHJlc3VsdHNfZGZgLiAgQnV0IGZpcnN0LCBtb3JlIGRhdGEgY2xlYW5pbmcuLi4NCg0KQ3JlYXRlIHNvbWUgbmV3IHZhcmlhYmxlcyB3aXRoIGBtdXRhdGVgLiAgQnVpbGQgYSAqKmZ1bGwqKiBVUkwgZnJvbSB0aGUgX3JlbGF0aXZlIFVSTCBwYXRoXyAoaS5lLiB0aGUgYHVybGAgdmVjdG9yKSBhbmQgdGhlIGRvbWFpbiBvciBfYmFzZSBVUkxfIG9mIHRoZSB0YXJnZXQgc2l0ZS4gIFNpbmNlIHdlIHNjcmFwZWQgdGhlIHJlbGF0aXZlIFVSTCAqKnBhdGgqKiwgd2UgaGF2ZSB0byBjb25zdHJ1Y3QgYSAqKmZ1bGwqKiBVUkwuDQoNCmBgYHtyfQ0KdXJsc190b19jcmF3bF9kZiA8LSByZXN1bHRzX2RmICU+JSANCiAgbXV0YXRlKGZ1bGxfdXJsID0gZ2x1ZTo6Z2x1ZSgiaHR0cDovL3d3dy52b25kZWwuaHVtYW5pdGllcy51dmEubmx7dXJsfSIpKSAlPiUgDQogIG11dGF0ZShmdWxsX3VybCA9IHN0cl9yZXBsYWNlX2FsbChmdWxsX3VybCwgIlxcLlxcLiIsICIiKSkgJT4lIA0KICBzZWxlY3QoZnVsbF91cmwpDQoNCnVybHNfdG9fY3Jhd2xfZGYgIA0KYGBgDQoNCkFzIHlvdSBjYW4gc2VlLCBhYm92ZSwgaXQncyByZWFsbHkgaGVscGZ1bCB0byBrbm93IGFib3V0IFRpZHl2ZXJzZSB0ZXh0IG1hbmlwdWxhdGlvbiwgc3BlY2lmaWNhbGx5IGBtdXRhdGVgLCAgW2BnbHVlYF0oaHR0cHM6Ly9nbHVlLnRpZHl2ZXJzZS5vcmcvKSwgYW5kIFtwYXR0ZXJuIG1hdGNoaW5nIGFuZCByZWdleF0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9zdHJpbmdzLmh0bWwpIHVzaW5nIHRoZSBbc3RyaW5nciBwYWNrYWdlXShodHRwczovL3N0cmluZ3IudGlkeXZlcnNlLm9yZy8pLg0KDQojIyMgT3BlcmF0aW9uYWxpemUgdGhlIHdvcmtmbG93DQoNClRvIG9wZXJhdGlvbmFsaXplIHRoaXMgcGFydCBvZiB0aGUgd29ya2Zsb3csIHlvdSB3YW50IHRvIGl0ZXJhdGUgb3ZlciB0aGUgdmVjdG9yIGBmdWxsX3VybGAgZm91bmQgaW4gdGhlIGB1cmxzX3RvX2NyYXdsX2RmYCB0aWJibGUuIFRoZW4gYHJlYWRfaHRtbGAgZm9yIGVhY2ggbmFtZSB0aGF0IGludGVyZXN0IHlvdS4gIFJlbWVtYmVyIHRoYXQgb25seSA1MCBvZiB0aGUgNTQgcm93cyBpbiB0aGUgYHJlc3V0bHNfZGZgIHRpYmJsZSBhcmUgdGFyZ2V0IG5hbWVzIHRvIGNyYXdsLiBTbywgcmVhbGx5LCB5b3Ugc3RpbGwgaGF2ZSBzb21lIGRhdGEgd3JhbmdsaW5nIHRvIGRvLiAgSG93IGNhbiB5b3UgZWxpbWluYXRlIHRoZSBmb3VyIHJvd3MgaW4gdGhlIGByZXN1bHRzX2RmYCB0aWJibGUgdGhhdCBhcmUgbm90IHRhcmdldHMgKGkuZS4gbmFtZXMpPyAgU29tZXdoZXJlLCBiZWxvdywgSSdsbCBhbHNvIHNob3cgeW91IGhvdyB0byBleGNsdWRlIHRoZSBmb3VyIHJvd3Mgb2YgdW5uZWNlc3NhcnkvdW5oZWxwZnVsIGluZm9ybWF0aW9uLg0KDQojIyBOYXZpZ2F0aW9uIGFuZCBjcmF3bGluZw0KDQpNZWFud2hpbGUsIHdlIHN0aWxsIGhhdmUgdGhlICoqZ29hbCB0byBzeXN0ZW1hdGljYWxseSBjcmF3bCB0aGUgc2l0ZSdzIG5hdmlnYXRpb24gbGlua3MqKiBmb3IgZWFjaCBvZiB0aGUgMjArIHN1bW1hcnkgcmVzdWx0cyBwYWdlcywgZWFjaCBvZiB3aGljaCBjb25zaXN0cyBvZiBmaWZ0eSBuYW1lcy4gIFJlbWVtYmVyLCBlYWNoIHN1bW1hcnkgcmVzdWx0cyBwYWdlIGFsc28gaGFzIGxpbmtzIHRvIHdlYiBzaXRlICoqbmF2aWdhdGlvbioqLiAgTmF2aWdhdGlvbiBsaW5rcyBhcmUgdGhlICoqa2V5IHRvIGNyYXdsaW5nKiogdGhyb3VnaCB0aGUgb3RoZXIgc3VtbWFyeSByZXN1bHRzIHBhZ2VzLiAgWW91IHdpbGwgbmVlZCB0byB3cml0ZSBjb2RlIHRoYXQgY3Jhd2xzIHRocm91Z2ggdGhlIG5hdmlnYXRpb24gbGlua3MsIHRoZW4gYHJlYWRfaHRtbCgpYCBmb3IgZWFjaCBvZiB0aGUgZmlmdHkgbmFtZS91cmxzIGluIHRoYXQgcGFydGljdWxhciBzdW1tYXJ5IHJlc3VsdHMgcGFnZS4gIA0KDQpUaGVyZWZvcmUsIG9uZSBjcmF3bGluZyAqKmdvYWwqKiBpcyB0byBidWlsZCB1cCBhIHRpYmJsZSB0aGF0IGNvbnRhaW5zIFVSTFMgZm9yIGVhY2ggc3VtbWFyeSByZXN1bHRzIHBhZ2UuICBTbyBmYXIgd2Ugb25seSBoYXZlIGxpbmtzIHRvIGZpZnR5IG5hbWVzIGZyb20gdGhlIGZpcnN0IHN1bW1hcnkgcmVzdWx0cyBwYWdlLiAgV2UgZG9uJ3QgaGF2ZSB0aGUgVVJMcyBmb3IgZWFjaCBvZiB0aGUgMjArIG5hdmlnYXRpb24gcGFnZXMuIEhvdyBkbyB3ZSBnZXQgdGhvc2U/IEZpcnN0IGZpbmQgdGhlIHBhdHRlcm4gZm9yIHRoZSBsaW5rcyB0byB0aGUgbmF2aWdhdGlvbiBwYWdlcy4gIExldCdzIGdldCB0aGUgbmF2aWdhdGlvbiBsaW5rIHRvIHRoZSBbc2Vjb25kIHN1bW1hcnkgcmVzdWx0cyBwYWdlXShodHRwOi8vd3d3LnZvbmRlbC5odW1hbml0aWVzLnV2YS5ubC9lY2FydGljby9wZXJzb25zL2luZGV4LnBocD9zdWJ0YXNrPWJyb3dzZSZmaWVsZD1zdXJuYW1lJnN0cnRjaGFyPUEmcGFnZT0yKS4gIA0KDQpgYGBodG1sDQpodHRwOi8vd3d3LnZvbmRlbC5odW1hbml0aWVzLnV2YS5ubC9lY2FydGljby9wZXJzb25zL2luZGV4LnBocD9zdWJ0YXNrPWJyb3dzZSZmaWVsZD1zdXJuYW1lJnN0cnRjaGFyPUEmcGFnZT0yDQpgYGANCg0KQWZ0ZXIgc29tZSBpbnZlc3RpZ2F0aW9uIG9mIHRoZSB0aGUgSFRNTCBzb3VyY2UsIEkgc2VlIHRoZSBvbmx5IGRpZmZlcmVuY2UgYmV3dGVlbiBuYXZpZ2F0aW9uIFVSTFMgaXMgdGhlIHZhbHVlIG9mIHRoZSBVUkwgYXJndW1lbnQgYHBhZ2U9YC4gIEV2ZW50dWFsbHkgd2UgbmVlZCB0byBjb25zdHJ1Y3QgYSB0aWJibGUgd2l0aCBhbGwgdGhlIFVSTHMgZm9yIGVhY2ggb2YgdGhlIG5hdmlnYXRpb24gcGFnZXMuICBCdXQgZmlyc3QsIGxldCdzIGxlYXJuIGEgYml0IG1vcmUgYWJvdXQgSFRNTCBwYXJzaW5nLg0KDQojIyMgRGVjb25zdHJ1Y3RpbmcgYSBzaW5nbGUgdGFyZ2V0IHBhZ2UgYW5kIHNpdGUgZGVzaWduDQoNClNvIGZhciB3ZSd2ZSB1c2VkIGBydmVzdGAgZnVuY3Rpb25zIGFuZCBsZWFybmVkIGEgbGl0dGxlIEhUTUwuICBZb3UgKiphbHNvIG5lZWQgdG8ga25vdyBhYm91dCoqIFsqKkNTUyoqIChDYXNjYWRpbmcgU3R5bGUgU2hlZXRzKV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ1NTKS4gIA0KDQpZb3UgY2FuIGZvbGxvdyB0aGUgbGluayBhYm92ZSBmb3IgYSBkZWZpbml0aW9uIG9mIENTUy4gIEFzIGEgcXVpY2tzdGFydCBhcHByYW9jaCwgSSByZWNvbW1lbmQgcGxheWluZyB0aGlzIFtDU1MgZ2FtZV0oaHR0cHM6Ly9mbHVrZW91dC5naXRodWIuaW8vKSBhcyBhIGZ1biBhbmQgcXVpY2sgd2F5IHRvIGxlYXJuIGp1c3QgZW5vdWdoIENTUy4gIEkgY29tcGxldGVkIHRoZSBmaXJzdCAxNSBsZXZlbHMgb2YgdGhlIGdhbWU7IHRoYXQgd2FzIGVub3VnaCB0byBnZXQgYSBnb29kIGZvdW5kYXRpb24gd2l0aCBDU1MuICBEZXBlbmRpbmcgb24gdGhlIGNvbXBsZXhpdHkgb2YgdGhlIEhUTUwgYW5kIENTUywgeW91IG1heSBuZWVkIHRvIGtub3cgYSBsaXR0bGUgbW9yZSBvciBhIGxpdHRsZSBsZXNzLiAgQnV0IHlvdSBuZWVkIHRvIGtub3cgc29tZXRoaW5nIGFib3V0IEhUTUwgYW5kIENTUy4NCg0KKipDU1MqKiB3aWxsIGhlbHAgdXMgc3Vic2V0IHRoZSBIVE1MIGZvciBhIHNpbmdsZSB0YXJnZXQgcGFnZS4gIEkgZGlkbid0IG1lbnRpb24gaXQgYmVmb3JlIGJ1dCwgeW91IG9mdGVuIG5lZWQgdG8gW3ZpZXcgdGhlIHNvdXJjZSBvZiB0aGUgSFRNTF0oaHR0cHM6Ly93d3cubGlmZXdpcmUuY29tL3ZpZXctaHRtbC1zb3VyY2UtaW4tY2hyb21lLTM0NjY3MjUpOyB1c2UgdGhlIFtjaHJvbWUgYnJvd3NlciB0byBpbnNwZWN0IGVsZW1lbnRzXShodHRwczovL3d3dy53aWtpaG93LmNvbS9JbnNwZWN0LUVsZW1lbnQtb24tQ2hyb21lKSBvZiBwYWdlcyBpbiBhIHdlYiBicm93c2VyOyBhbmQgdXNlIHRoZSBjaHJvbWUgYnJvd3NlciBleHRlbnNpb24sIFtzZWxlY3RvciBnYWRnZXRdKGh0dHBzOi8vcnZlc3QudGlkeXZlcnNlLm9yZy9hcnRpY2xlcy9zZWxlY3RvcmdhZGdldC5odG1sKSwgdG8gYmV0dGVyIHVuZGVyc3RhbmQgdGhlIEhUTUwgYW5kIENTUyB0YWdnaW5nIGFuZCBzdHJ1Y3R1cmUuICANCg0KVGhpcyBpcyBrZXkuICBXaGVuIHdlYiBzY3JhcGluZywgeW91IGFyZSBlZmZlY3RpdmVseSByZXZlcnNlIGVuZ2luZWVyaW5nIHRoZSB3ZWIgc2l0ZS4gIFRvIHJldmVyc2UgZW5naW5lZXIgaXQsIHlvdSBtdXN0IGhhdmUgYSBzb2xpZCB1bmRlcnN0YW5kaW5nIG9mIHRoZSB0YXJnZXQgc2l0ZSdzIHN0cnVjdHVyZSwgdGhlIHNpdGUgbmF2aWdhdGlvbiwgSFRNTCwgYW5kIENTUy4gIEtub3dsZWRnZSBvZiB0aGUgc2l0ZSBzdHJ1Y3R1cmUgaXMgaW5kZXBlbmRlbnQgb2YgUiwgYHJ2ZXN0YCwgX2xpc3RzXywgYW5kIGBwdXJycmAuDQoNCkFueXdheSwgYW4gZXhhbXBsZSBvZiBhIENTUyBlbGVtZW50IGluIHRoZSByZXN1bHRzIHBhZ2UgaXMgJ2NsYXNzJyBhdHRyaWJ1dGUgb2YgdGhlIGA8ZGl2PmAgdGFnLiAgSW4gdGhlIGNhc2UgYmVsb3cgd2UgYWxzbyBoYXZlIGEgY2xhc3MgdmFsdWUgb2YgInN1Ym5hdiIuICBWaWV3aW5nIHRoZSBzb3VyY2UgSFRNTCBvZiBvbmUgc3VtbWFyeSByZXN1bHRzIHBhZ2Ugd2lsbCBzaG93IHRoZSBgPGRpdj5gIHRhZ3Mgd2l0aCB0aGUgQ1NTIF9jbGFzc18gZWxlbWVudC4NCg0KKipGb3IgRXhhbXBsZSoqICAgDQoNClVzZSBpcyBgaHRtbF9ub2RlcygpYCB3aXRoIGBodG1sX3RleHQoKWAsIGFuZCBgaHRtbF9hdHRyKClgIHRvIHBhcnNlIHRoZSBhbmNob3IgdGFnIG5vZGVzLCBgPGE+YCwgZm91bmQgd2l0aGluIHRoZSBgPGRpdj5gIHRhZ3Mgd2hpY2ggY29udGFpbiB0aGUgYGNsYXNzPSJzdWJuYXYiYCBhdHRyaWJ1dGUuDQoNCmBgYGh0bWwNCjxkaXYgY2xhc3MgPSAic3VibmF2Ij4NCiAgICAuLi4NCiAgICA8YSBocmVmPSIvZWNhcnRpY28vcGVyc29ucy9pbmRleC5waHA/c3VidGFzaz1icm93c2UmYW1wO2ZpZWxkPXN1cm5hbWUmYW1wO3N0cnRjaGFyPUEmYW1wO3BhZ2U9MyI+WzEwMS0xNTBdPC9hPg0KICAgIC4uLg0KPC9kaXY+DQpgYGANCg0KKipDT0RFKiogIA0KDQpQYXJzZSB0aGUgKip0ZXh0Kiogb2YgdGhlIG5hdmlnYXRpb24gYmFyLg0KDQpgYGB7cn0NCnJlc3VsdHMgJT4lICNodG1sX25vZGVzKCJkaXYuc3VibmF2IikNCiAgaHRtbF9ub2RlcygiZGl2LnN1Ym5hdiBhIikgJT4lIA0KICBodG1sX3RleHQoKQ0KDQpgYGANCg0KUGFyc2UgdGhlIEhUTUwgX2hyZWZfICoqYXR0cmlidXRlKiogdG8gZ2V0IHRoZSBVUkwuDQoNCmBgYHtyIGxvb3B9DQpuYXZpZ2F0aW9uIDwtIHJlc3VsdHMgJT4lIA0KICBodG1sX25vZGVzKCJkaXYuc3VibmF2IGEiKSAlPiUgDQogIGh0bWxfYXR0cigiaHJlZiIpDQoNCm5hdmlnYXRpb24NCmBgYA0KDQojIyMgQ3Jhd2wgc3VtbWFyeSByZXN1bHRzIHBhZ2VzDQoNCldlIHdhbnQgdG8gZ2FpbiBhIGNsZWFyIHNlbnNlIG9mIHRoZSBwcmVkaWN0YWJsZSBuYXZpZ2F0aW9uIHN0cnVjdHVyZSBvZiBvdXIgdGFyZ2V0IHNpdGUsIGFuZCB0aGUgbmF2aWdhdGlvbiBVUkxzLCBzbyB0aGF0IHdlIGNhbiAqKmNyYXdsKiogdGhyb3VnaCBlYWNoIHBhZ2Ugb2YgdGhlIHRhcmdldCBzaXRlLiAgQWdhaW4sIHRoZSB0YXNrIGJlZm9yZSB1cyBpcyB0byBkbyB0aGlzIGNyYXdsaW5nIHN5c3RlbWF0aWNhbGx5LiAgTGV0J3MgcHV0IHRoZSBgbmF2aWdhdGlvbmAgb2JqZWN0IGludG8gYSB0aWJibGUgZGF0YS1mcmFtZSBjYWxsZWQgYG5hdl9kZmAuDQoNCmBgYHtyfQ0KbmF2X2RmIDwtIHRpYmJsZShuYXZpZ2F0aW9uKQ0KbmF2X2RmDQpgYGANCg0KSW4gdGhlIGFib3ZlIHRpYmJsZSwgSSBzZWUgSSBuZWVkIHRvICoqY2xlYW4qKiB0aGUgYG5hdl9kZiRuYXZpZ2F0aW9uYCB2ZWN0b3Igc28gdGhhdCBvbmx5IHRoZSB1bmlxdWUgVVJMcyBmb3IgZWFjaCA1MC1yZXN1bHQgc3VtbWFyeSBwYWdlIHJlbWFpbi4gIE9uZSBwcm9ibGVtIGlzIHRoZXJlIGFyZSBvbmx5IDE0IHJvd3MgYW5kIGEgbG90IG9mIHJlZHVuZGFuY3kgYW5kIG1hbnkgbWlzc2luZyBzdW1tYXJ5IHBhZ2UgVVJMUy4gIE9uZSByZWFzb24gSSBrbm93IHRoaXMgaXMgdGhhdCBJIGhhdmUgYSBsaW5rIGZvciBwYWdlIDIsIDMsIDQsIDUsIGFuZCAyMjsgYnV0IG5vdGhpbmcsIG5vIFVSTHMgZm9yIHBhZ2VzIGJldHdlZW4gcGFnZXMgNiBhbmQgMjEuICBUaGlzIG1pcnJvcnMgd2hhdCB3ZSBzZWUgd2hlbiB3ZSBbdmlldyB0aGUgYWN0dWFsIHRhcmdldCB3ZWIgcGFnZSBpbiBhIHdlYiBicm93c2VyXShodHRwOi8vd3d3LnZvbmRlbC5odW1hbml0aWVzLnV2YS5ubC9lY2FydGljby9wZXJzb25zL2luZGV4LnBocD9zdWJ0YXNrPWJyb3dzZSkuICBUaGF0IGlzLCBJIGhhdmUgbGlua3MgZm9yICoqcmVzdWx0cyoqIFsxLTUwXSwgWzUxLTEwMF0sIFsxMDAtMTUwXSwgYnV0IG5vdGhpbmcgZm9yIFsyNTEtMjYwXSAoaS5lLiBwYWdlIDYpLCBldGMuICBUaGUgdGFyZ2V0IHBhZ2UgaXMgY29tcG9zZWQgb2YgSFRNTCBhbmQgQ1NTIGFuZCBjb21wcmlzZXMgd2hhdCB3ZSBoYXZlIGluIG91ciBpbXBvcnRlZCBgcmVzdWx0c2Agb2JqZWN0LCBhYm92ZS4gIFRvIGNvbmZpcm0gbXkgaW52ZXN0aWdhdGlvbiwgSSdsbCB1c2UgYSB3ZWIgYnJvd3NlciB0byAqKnZpZXcqKiB0aGUgSFRNTCAqKnNvdXJjZSoqIG9mIG9uZSBvZiB0aGUgc3VtbWFyeSBwYWdlcy4gIFRoZSB1bnBhcnNlZCBIVE1MIGlzIHdoYXQgeW91IGhhdmUgaW4gYHJlc3VsdHNgIG9iamVjdC4gIA0KDQpXaGF0IEkgc2VlIGluIHRoZSB2aWV3IHNvdXJjZSBvciB0aGUgYG5hdl9kZiRuYXZpZ2F0aW9uYCB2ZWN0b3IgaXMgdGhhdCBlYWNoIG5hdmlnYXRpb24gVVJMIGlzIHRoZSBzYW1lLCAqKmV4Y2VwdCoqIGZvciB0aGUgZGlmZmVyZW5jZSBpbiB0aGUgYHBhZ2U9YCBlbGVtZW50IGZvdW5kIGF0IHRoZSBlbmQgb2YgdGhlIFVSTCBhcmd1bWVudC4gIA0KDQojIyMgTWluaSByZXZpZXcgc3VtbWFyeQ0KDQpXZSBjYW4gc2VlIHRoYXQgdGhlIG5hdmlnYXRpb24gVVJMIHBhdHRlcm4gaXMgcHJlZGljdGFibGUuICBXZSBjYW4gY29uc3RydWN0IGEgZnVsbCBVUkwgZm9yIGVhY2ggc3VtbWFyeSByZXN1bHRzIHRhcmdldCBwYWdlLiBXZSBjYW4gbWFrZSBhIHRpYmJsZSB3aXRoIGFsbCB0aGVzZSBsaW5rcywgb25lIHJvdyBmb3IgZWFjaCBVUkwgdG8gZWFjaCBuYXZpZ2F0aW9uIHNlY3Rpb24gb2YgZWFjaCBzdW1tYXJ5IHJlc3VsdHMgcGFnZSBvZiB0aGUgdGFyZ2V0IHNpdGUuIE9uY2Ugd2UgaGF2ZSB0aGlzLCB3ZSBjYW4gd3JpdGUgYSBzY3JpcHQgdG8gc3lzdGVtYXRpY2FsbHkgYnJvd3NlLCBvciBjcmF3bCwgdGhyb3VnaCB0aGUgdGFyZ2V0IHNpdGUganVzdCBhcyBpZiB3ZSBtYW51YWxseSBtb3VzZS1jbGlja2VkIGVhY2ggbGluayBpbiBhIHdlYiBicm93c2VyLiAgV2Ugd2lsbCB1c2UgdGlkeXZlcnNlIHBhY2thZ2VzIHRvIGNyYXdsIHRocm91Z2ggdGhlIHNpdGUgYW5kIGBydmVzdDo6aHRtbF9yZWFkKClgIHRvIGltcG9ydCB0aGUgSFRNTC4gIEFmdGVyIHdlIGNyYXdsIGVhY2ggcGFnZSwgd2UnbGwgcGFyc2UgdGhlIHRhcmdldCBIVE1MIGRhdGEuDQoNCiMjIyBEZXZlbG9wIGEgcGxhbiANCg0KKipHb2FsKiogbWFrZSBhIHRpYmJsZSB3aXRoIGFsbCB0aGUgbmF2aWdhdGlvbiBsaW5rcw0KDQpMZXQncyBzdGFydCBieSBpbXBvcnRpbmcgdGhlIEhUTUwgb2YgYSBzaW5nbGUgc3VtbWFyeSByZXN1bHRzIHBhZ2UuICBUaGVuIEknbGwgbWFrZSBhIHRpYmJsZSBvZiB0aGUgbmF2aWdhdGlvbiBsaW5rcyBmb3VuZCB3aXRoaW4gdGhlIGdhdGhlcmVkIGFuZCByZWxldmFudCBgPGRpdj5gIEhUTUwgdGFncywgaS5lLiB0aGUgZGl2IHRhZ3Mgd2l0aCB0aGUgX3N1Ym5hdl8gQ1NTIGNsYXNzLiAgV2UgZGlkIHRoYXQgd2l0aCB0aGUgYG5hdl9kZmAgdGliYmxlLg0KDQpBZnRlciB3ZSBnYXRoZXIgdGhvc2UgbmF2aWdhdGlvbiBsaW5rcyBpbiBgbmF2X2RmJG5hdmlnYXRpb25gLCB3ZSB3aWxsIHdyYW5nbGUgYW5kIGV4cGFuZCB0aGUgdmVjdG9yIHRvIGluY2x1ZGUgb25seSB0aGUgcmVsZXZhbnQgbmF2aWdhdGlvbiBVUkxzLiAgQnV0LCB3aHkgZGlkIHdlIGhhdmUgZHVwbGljYXRlIG5hdmlnYXRpb24gVVJMUyBpbiB0aGF0IHZlY3Rvcj8gIExvb2sgYXQgdGhlIFtzdW1tYXJ5IHBhZ2VdKGh0dHA6Ly93d3cudm9uZGVsLmh1bWFuaXRpZXMudXZhLm5sL2VjYXJ0aWNvL3BlcnNvbnMvaW5kZXgucGhwP3N1YnRhc2s9YnJvd3NlKSBpbiBhIHdlYiBicm93c2VyLiAgU2VlIGFueSBkdXBsaWNhdGlvbj8gIElmIG5vdCwgbG9vayBjbG9zZXIuDQoNCg0KQmVsb3csIHVzZSBgZHBseXI6OmRpc3RpbmN0KClgIGFuZCBgc3RyaW5ncjo6c3RyaW5nX2V4dHJhY3QoKWAgYW1vbmcgb3RoZXIgdGlkeXZlcnNlIGZ1bmN0aW9ucyB0byB3cmFuZ2xlIGEgdGliYmxlIG9mIHJlbGV2YW50IGxpbmtzLiAgDQoNCmBgYHtyfQ0KbmF2X2RmIDwtIG5hdl9kZiAlPiUNCiAgZmlsdGVyKHN0cl9kZXRlY3QobmF2aWdhdGlvbiwgIiZwYWdlPSIpKSAlPiUNCiAgZGlzdGluY3QobmF2aWdhdGlvbikgJT4lDQogIG11dGF0ZShwYWdlX25vID0gc3RyX2V4dHJhY3QobmF2aWdhdGlvbiwgIlxcZCskIikpICU+JQ0KICBtdXRhdGUocGFnZV9ubyA9IGFzLm51bWVyaWMocGFnZV9ubykpDQoNCm5hdl9kZg0KYGBgDQoNClRoZXJlJ3Mgc29tZSBfZmFuY3lfIHJlZ2V4IHBhdHRlcm4gbWF0Y2hpbmcgdGFraW5nIHBsYWNlIGluIHRoZSBkYXRhIHdyYW5nbGluZyBjb2RlIGNodW5rLCBhYm92ZS4gIFJlbWVtYmVyIGhvdyBJIHNhaWQgeW91IG5lZWQgdG8gbGVhcm4gYWJvdXQgcGF0dGVybiBtYXRjaGluZyBhbmQgdGhlIHN0cmluZ3IgbGlicmFyeT8gIFl1cC4NCg0KQW55d2F5Li4uDQoNCk5vdyBhbGwgd2UgbmVlZCB0byBkbyBpcyBleHBhbmQgdGhlIHNlcXVlbmNlIG9mIHBhZ2VzLiAgKEhpbnQ6IGB0aWR5cjo6ZXhwYW5kKClgICkNCg0KVGhlIG1heGltdW0gbnVtYmVyIG9mIHN1bW1hcnkgcGFnZXMgaW4gdGhlIGBuYXZpZ2F0aW9uJHBhZ2Vfbm9gIHZhcmlhYmxlIGlzIDIyLiAgVGhpcyBzaG91bGQgbWVhbiB0aGUgbWF4aW11bSBudW1iZXIgb2YgVVJMcyB0byBzdW1tYXJ5IHJlc3VsdHMgcGFnZXMgd2lsbCBiZSByb3VnaGx5IDIyLiBSZWdhcmRsZXNzIG9mIHRoZSB0b3RhbCBudW1iZXIgb2YgdGFyZ2V0IG5hbWVzL3BhZ2VzLCBvdXIgdGFzayBpcyB0byBidWlsZCBhIHRpYmJsZSB3aXRoIGEgVVJMIGZvciBlYWNoIHN1bW1hcnkgcmVzdWx0cyBwYWdlLCBpLmUuIHBhZ2VzIDEgdGhvcm91Z2ggMjIuICBJRiB3ZSBoYXZlIGEgbGluayB0byBlYWNoIHN1bWFtcnkgcmVzdWx0cyBwYWdlLCB0aGVuIGNhbiB3ZSBnZXQgYSBsaW5rIGZvciBlYWNoIG9mIHRoZSBmaWZ0eSBwZW9wbGUgbGlzdGVkIG9uIGVhY2ggb2YgdGhlIHN1bW1hcnkgcmVzdWx0IHBhZ2VzLiAgDQoNCkhvbmVzdGx5LCBidWlsZGluZyB0aGlzIGxpc3Qgb2YgbmF2aWdhdGlvbiBVUkxzIHRha2VzIHNvbWUgZWZmb3J0IGluIFIsIGVzcGVjaWFsbHkgaWYgeW91J3JlIG5ldyB0byBSLiAgU28sIG1heWJlLCB0aGVyZSdzIGFuIGVhc2llciB3YXkuICBJdCBtaWdodCBiZSBlYXNpZXIgdG8gYnVpbGQgdGhlIHJhbmdlIG9mIHN1bW1hcnkgcGFnZSBVUkxzIGluIEV4Y2VsLCB0aGVuIGltcG9ydCB0aGUgZXhjZWwgZmlsZSAob3IgQ1NWIGZpbGUpIG9mIFVSTHMgaW50byBSIGZvciBjcmF3bGluZyB2aWEgYHJ2ZXN0YC4gIEJ1dCBJIHdhbnQgYSByZXByb2R1Y2libGUsIGNvZGUtYmFzZWQgYXBwcm9hY2guICANCg0KU2VlIGV4YW1wbGUgY29kZSwgYmVsb3csIGZvciBhIHJlcHJvZHVjaWJsZSBleGFtcGxlLiAgV2l0aCB0aGUgbmV4dCBjb2RlIGNodW5rLCB5b3UgY2FuIHVzZSBUaWR5dmVyc2UgdGVjaG5pcXVlcyBhbmQgYnVpbGQgYSB0aWJibGUgb2YgdXJscyB0byBpdGVyYXRlIG92ZXIuIEluIHRoaXMgY2FzZSwgdGhlIGltcG9ydGFudCByZXByb2R1Y2libGUgc3RlcCB1c2UgYHN0cmluZ3I6OnN0cl9leHRyYWN0KClgIHRvIGZpbmQgYW5kIG1hdGNoIHRoZSBVUkwgcGF0dGVybi4gIA0KDQpgYGB7cn0NCm5hdl9kZiA8LSBuYXZfZGYgJT4lIA0KICBtdXRhdGUobmF2aWdhdGlvbiA9IHN0cl9leHRyYWN0KG5hdmlnYXRpb24sICIuKig/PVxcPVxcZCskKSIpKSAlPiUgDQogIG11dGF0ZShwYWdlX25vID0gYXMuaW50ZWdlcihzdHJfcmVwbGFjZShwYWdlX25vLCAiXjIkIiwgIjEiKSkpICU+JSANCiAgZXhwYW5kKG5hdmlnYXRpb24sIHBhZ2Vfbm8gPSBmdWxsX3NlcShwYWdlX25vLCAxKSkgJT4lIA0KICB0cmFuc211dGUodXJsID0gZ2x1ZTo6Z2x1ZSgiaHR0cDovL3d3dy52b25kZWwuaHVtYW5pdGllcy51dmEubmx7bmF2aWdhdGlvbn09e3BhZ2Vfbm99IikpDQoNCiMgdGhlIHJlZ3VsYXIgZXhwcmVzc2lvbiAnLiooPz1cXD1cXGQrJCknIGNhbiBiZSByZWFkIGFzOiANCiMgY2FwdHVyZS9maW5kIGRhdGEgZm91bmQgQkVGT1JFICcoPz08PHBhdHRlcm4+PiknIHRoZSBwYXR0ZXJuLiAgDQojIFRoZSBwYXR0ZXJuIGlzID08ZGlnaXQ+PGRpZ2l0PiwgDQojIHRoZSAnPScgc2lnbiBoYXMgdG8gYmUgImVzY2FwZWQiICAnXFw9Jy4gIA0KIyBUaGUgcHJlY2lzZSByZWdleCBwYXR0ZXJuIHN5bnRheCBoYXMgdGhlIGVxdWFsIHNpZ24gZm9sbG93ZWQgYnkgYSBkaWdpdCAnXFxkJy4NCiMgbXVsdGlwbGUgZGlnaXRzICAnKycsICBpLmUuICdcXGQrJw0KIyBTaW5jZSB0aGUgZGlnaXRzIGFyZSBmb3VuZCBhdCB0aGUgZW5kIG9mIHRoZSB2YWx1ZSBvZiB0aGUgdmFyaWFibGUsIHVzZSBhbiBhbmNob3IgDQojIFRvIGFuY2hvciB0aGUgZW5kIG9mIHRoZSByZWdleCBwYXR0ZXJuLCB1c2UgdGhlICckJyByZWdleCBhbmNob3Igbm90YXRpb24uDQojIFJlbWVtYmVyLCB3ZSBhcmUgbWF0Y2hpbmcgZXZlcnl0aGluZyBiZWZvcmUgdGhlIHBhdHRlcm4uICBNYXRjaCB3aXRoIHRoZSB3aWxkY2FyZDogJy4qJw0KIyBIb25lc3RseSwgdGhlcmUncyBubyBzdWJzdGl0dXRlIGZvciBrbm93aW5nIGEgYml0IGFib3V0IHJlZ2V4IHdoZW4geW91J3JlIGNvZGluZy4NCiMNCiMgU2VlIHRoZSAid29yayB3aXRoIHN0cmluZ3MiIGNoZWF0c2hlZXQuICBodHRwczovL3JzdHVkaW8uY29tL3Jlc291cmNlcy9jaGVhdHNoZWV0cy8NCg0KbmF2X2RmDQpgYGANCg0KDQoNCiMjIEl0ZXJhdGUNCg0KVXNlIGBwdXJycjo6bWFwYCAqKmluc3RlYWQqKiBvZiAqKidmb3InKiogbG9vcHMuICBCZWNhdXNlIHB1cnJyIGlzIHRoZSBSL1RpZHl2ZXJzZSB3YXkuICAnRm9yJyBsb29wcyBhcmUgZmluZSwgYnV0IGludmVzdCBzb21lIHRpbWUgbGVhcm5pbmcgcHVycnIgYW5kIHlvdSdsbCBiZSBiZXR0ZXIgb2ZmLiAgU3RpbGwsIHRoZXJlJ3Mgbm8gd3Jvbmcgd2F5IHRvIGl0ZXJhdGUgYXMgbG9uZyBhcyB5b3UgZ2V0IHRoZSByaWdodCBhbnN3ZXIuICBTbywgZG8gd2hhdCB3b3Jrcy4gIEJlbG93IGlzIHRoZSBUaWR5dmVyc2UvUHVycnIgd2F5Li4uLg0KDQpOb3cgdGhhdCBJIGhhdmUgYSBmdWxsIGxpc3Qgb2YgbmF2aWdhdGlvbiBVUkxzLCBlYWNoIG9mIHdoaWNoIHJlcHJlc2VudHMgYSB3ZWIgcGFnZSB0aGF0IGhhcyBhIHN1bW1hcnkgb2YgNTAgbmFtZXMvbGlua3MuICBNeSBuZXh0IHRhc2sgaXMgdG8gcmVhZCB0aGUgSFRNTCBvZiBlYWNoIFVSTCByZXByZXNlbnRpbmcgYSB0YXJnZXQtbmFtZS4gIEJ5IHJlYWRpbmcgdGhlIFVSTCAoaW1wb3J0aW5nIHRoZSBIVE1MKSBmb3IgZWFjaCB0YXJnZXQgbmFtZSwgSSB3aWxsIHRoZW4gaGF2ZSBIVE1MIGZvciBlYWNoIGluZGl2aWR1YWwgdGFyZ2V0IHBlcnNvbiBpbiB0aGUgZGF0YWJhc2UuICBPZiBjb3Vyc2UsIEkgc3RpbGwsIHRoZW4sIGhhdmUgdG8gcmVhZCBhbmQgcGFyc2UgdGhlIEhUTUwgb2YgdGhvc2UgdGFyZ2V0LW5hbWUgcGFnZXMsIGJ1dCBJIGNhbiBkbyB0aGF0LiAgVGhlIHNjcmFwaW5nIChjcmF3bGluZyArIHBhcnNpbmcpIHdvcmtzIHdoZW4gSSBoYXZlIGEgVVJMIHBlciB0YXJnZXQgcGVyc29uLiAgSGF2aW5nIGEgVVJMIGZvciBlYWNoIHRhcmdldCBwZXJzb24gbWVhbnMgSSBjYW4gc3lzdGVtYXRpY2FsbHkgc2NyYXBlIHRoZSB3ZWIgc2l0ZS4gIEluIG90aGVyIHdvcmRzLCBJIGNyYXdsIHRoZSBzdW1tYXJ5IG5hdmlnYXRpb24gdG8gY29uc3RydWN0IGEgVVJMIGZvciBlYWNoIHN1bW1hcnkgcGFnZS4gIFRoZW4gSSBpbXBvcnQgSFRNTCBmb3IgZWFjaCBzdW1tYXJ5IHBhZ2UgdG8gZ2V0IGEgVVJMIHRvIGVhY2ggcGVyc29uJ3MgcGFnZS4gIFRoZW4gSSBpbXBvcnQgZWFjaCBwZXJzb24ncyBwYWdlIGFuZCBwYXJzZSB0aGUgSFRNTCBmb3IgZWFjaCBwZXJzb24ncyByZWNvcmQuDQoNCkJ1dCwgYmFjayB0byB0aGUgY3VycmVudCB0YXNrOiAgaW1wb3J0IHRoZSBIVE1MIGZvciBlYWNoIHN1bW1hcnkgcmVzdWx0cyBwYWdlIG9mIDUwIHJlY29yZHMuLi4NCg0KKipOb3RlKio6IHRoYXQsIGJlbG93LCBJIGludHJvZHVjZSBhICoqcGF1c2UqKiAoYFN5cy5zbGVlcCgpYCkgaW4gZnJvbnQgb2YgZWFjaCBgcmVhZF9odG1sKClgIGZ1bmN0aW9uLiAgVGhpcyBpcyBhIGNvbW1vbiB0ZWNobmlxdWUgZm9yIHdlbGwgYmVoYXZlZCB3ZWIgc2NyYXBpbmcuICBQYXVzaW5nIGJlZm9yZSBlYWNoIGByZWFkX2h0bWxgIGZ1bmN0aW9uLCBhdm9pZHMgb3ZlcndoZWxtaW5nIG15IHRhcmdldCdzIHNlcnZlci9uZXR3b3JrIGluZnJhc3RydWN0dXJlLiAgSWYgSSBvdmVyd2hlbG0gdGhlIHRhcmdldCBzZXJ2ZXIsIHRoZSBzZXJ2ZXIgaG9zdC1wZW9wbGUgbWF5IGNvbnNpZGVyIG1lIGEgRE5TIGF0dGFjay4gIElmIHRoZXkgdGhpbmsgSSdtIGEgRE5TIGF0dGFja2VyLCB0aGV5IG1pZ2h0IGNob29zZSB0byBibG9jayBteSBjb21wdXRlciBmcm9tIGNyYXdsaW5nIHRoZWlyIHNpdGUuICBJZiB0aGF0IGhhcHBlbnMsIEknbSB1cCBhIGNyZWVrLiAgSSBkb24ndCB3YW50IHRoYXQuICBJIHdhbnQgbXkgc2NyaXB0IHRvIGJlIGEgd2VsbCBiZWhhdmVkIGJvdC1jcmF3bGVyLiAgDQoNClNwZWFraW5nIG9mIGJlaW5nIGEgZ29vZCBhbmQgaG9ub3JhYmxlIHNjcmFwZXItY2l0aXplbiwgZGlkIEkgYnJvd3NlIHRoZSBbcm9ib3RzLnR4dF0oaHR0cDovL3d3dy52b25kZWwuaHVtYW5pdGllcy51dmEubmwvcm9ib3RzLnR4dCkgcGFnZSBmb3IgdGhlIHNpdGU/ICBEaWQgSSBjaGVjayB0aGUgc2l0ZSBmb3IgYSBUZXJtcyBvZiBTZXJ2aWNlIHBhZ2U/ICBEaWQgSSBsb29rIHRvIHNlZSBpZiB0aGVyZSB3ZXJlIGFueSB3cml0dGVuIHByb2hpYml0aW9ucyBhZ2FpbnN0IHdlYiBjcmF3bGluZywgc3lzdGVtYXRpYyBkb3dubG9hZGluZywgY29weXJpZ2h0LCBvciBsaWNlbnNpbmcgcmVzdHJpY3Rpb25zPyAgSSBkaWQgYW5kIHlvdSBzaG91bGQgdG9vLiAgQXMgb2YgdGhpcyB3cml0aW5nLCB0aGVyZSBkbyBub3QgYXBwZWFyIHRvIGJlIGFueSByZXN0cmljdGlvbnMgZm9yIHRoaXMgc2l0ZS4gIFlvdSBzaG91bGQgcGVyZm9ybSB0aGVzZSB0eXBlcyBvZiBnb29kLXNjcmFwaW5nIGh5Z2llbmUgc3RlcHMgZm9yIGV2ZXJ5IHNpdGUgeW91IHdhbnQgdG8gc2NyYXBlIQ0KDQpOb3RlOiAgQmVsb3csIGZvciBkZXZlbG9wbWVudCBwdXJwb3NlcywgSSBsaW1pdGVkIG15IGNyYXdsaW5nIHRvIDMgcmVzdWx0cyBsaW5rczogIGBteV91cmxfZGYkdXJsWzE6M11gLiAgQmUgY29uc2VydmF0aXZlIGR1cmluZyB5b3VyIGNvZGUgZGV2ZWxvcG1lbnQgdG8gYXZvaWQgYXBwZWFyaW5nIGFzIGEgRE5TIGF0dGFja2VyLiAgTGF0ZXIsIHdoZW4geW91IGFyZSByZWFkeSB0byBjcmF3bCB5b3VyIHdob2xlIHRhcmdldCBzaXRlLCB5b3UnbGwgd2FudCB0byByZW1vdmUgc3VjaCBsaW1pdHMgKGkuZS4gYFsxOjNdYC4pICBCdXQgZm9yIG5vdywgZG8gZXZlcnlvbmUgYSBmYXZvciBhbmQgdHJ5IG5vdCB0byBiZSBvdmVyIGNvbmZpZGVudC4gIFN0YXkgaW4gdGhlIGtpZGRpZSBwb29sLiAgRG8geW91ciBkZXZlbG9wbWVudCB3b3JrIHVudGlsIHlvdSBhcmUgc3VyZSB5b3UncmUgbm90IGFjY2lkZW50YWxseSB1bmxlYXNoaW5nIGEgbWFsaWNpb3VzIG9yIHBvb3JseSBjb25zdHJ1Y3RlZCB3ZWIgY3Jhd2xlci4NCg0KTm90ZTogIEJlbG93LCBJIGFtIGtlZXBpbmcgdGhlIG9yaWdpbmFsIHRhcmdldCBVUkwgdmFyaWFibGUsIGBzdW1tYXJ5X3VybGAsIGZvciBsYXRlciByZWZlcmVuY2UuICBUaGlzIHdheSBJIHdpbGwgaGF2ZSBhIHJlY29yZCBvZiB3aGljaCBwYXJzZWQgZGF0YSByZXN1bHRzIGNhbWUgZnJvbSB3aGljaCBVUkwgd2ViIHBhZ2UuICANCg0KTm90ZTogIEJlbG93LCB0aGUgZmluYWwgcmVzdWx0IGlzIGEgdGliYmxlIHdpdGggYSB2ZWN0b3IsIGBzdW1tYXJ5X3VybGAsIGFuZCBhbiBhc3NvY2lhdGVkIGNvbHVtbiBvZiBIVE1MIHJlc3VsdHMsIGVhY2ggcmVzdWx0IGlzIHN0b3JlZCBhcyBhIG5lc3RlZCBSIF9saXN0Xy4gIFRoYXQgaXMsIGEgY29sdW1uIG9mIGRhdGEgdHlwZXMgdGhhdCBhcmUgYWxsICJfbGlzdHNfIiwgYWthIGEgIl9saXN0IGNvbHVtbl8iLiAgUGVyc29uYWxseSBJIGZpbmQgbGlzdHMgdG8gYmUgYSBwYWluLiBJIHByZWZlciB3b3JraW5nIHdpdGggdGliYmxlcyAoYWthIF9kYXRhIGZyYW1lc18uKS4gIEJ1dCBfbGlzdHNfIGFwcGVhciBvZnRlbiBpbiBSIGRhdGEgd3JhbmdsaW5nLCBlc3BlY2lhbGx5IHdoZW4gc2NyYXBpbmcgd2l0aCBgcnZlc3RgLiAgVGhlIG1vcmUgeW91IHdvcmsgd2l0aCBfbGlzdHNfLCB0aGUgbW9yZSB5b3UgY29tZSB0byB0b2xlcmF0ZSBfbGlzdHNfIGZvciB0aGUgZmxleGlibGUgZGF0YSB0eXBlIHRoYXQgdGhleSBhcmUuICBBbnl3YXksIGlmIEkgd2VyZSB0byBsb29rIGF0IG9ubHkgdGhlIGZpcnN0IHJvdyBvZiByZXN1bHRzIGZyb20gdGhlIGh0bWxfcmVzdWx0cyBjb2x1bW4sIGBuYXZfcmVzdWx0c19saXN0JGh0bWxfcmVzdWx0c1sxXWAsIEkgd291bGQgZmluZCBhIF9saXN0XyBvZiB0aGUgcmF3IEhUTUwgZnJvbSB0aGUgZmlyc3Qgc3VtbWFyeSByZXN1bHRzIHBhZ2UgaW1wb3J0ZWQgdmlhIGByZWFkX2h0bWwoKWAuICANCg0KUmVjYXBwaW5nOiAgVGhpcyBpcyB0ZXN0aW5nLiBJIGhhdmUgdGhyZWUgVVJMcyBgKGh0bWxfcmV1bHRzWzE6M10pYCwgb25lIGZvciBlYWNoIG9mIHRoZSBmaXJzdCB0aHJlZSBuYXZpZ2F0aW9uIHN1bW1hcnkgcGFnZXMuICBFYWNoIHN1bW1hcnkgcGFnZSB3aWxsIGNvbnRhaW4gdGhlIHJhdyBIVE1MIGZvciA1MCBuYW1lcy4gSSB3aWxsIGByZWFkX2h0bWxgIGVhY2ggbGluaywgd2FpdGluZyAyIHNlY29uZHMgYmV0d2VlbiBlYWNoIGByZWFkX2h0bWxgLiAgDQoNCmBgYHtyfQ0KbmF2X3Jlc3VsdHNfbGlzdCA8LSB0aWJibGUoDQogIGh0bWxfcmVzdWx0cyA9IG1hcChuYXZfZGYkdXJsWzE6M10sDQogICAgfiB7DQogICAgICAjdXJsWzE6M10gLSBsaW1pdGluZyB0byB0aGUgZmlyc3QgdGhyZWUgc3VtbWFyeSByZXN1bHRzIHBhZ2VzIChlYWNoIHBhZ2UgPSA1MCByZXN1bHRzKQ0KICAgICAgU3lzLnNsZWVwKDIpDQogICAgICAjIERPIFRISVMhICBzbGVlcCAyIHdpbGwgcGF1c2UgMiBzZWNvbmRzIGJldHdlZW4gc2VydmVyIHJlcXVlc3RzIHRvIGF2b2lkIGJlaW5nIGlkZW50aWZpZWQgYW5kIHBvdGVudGlhbGx5IGJsb2NrZWQgYnkgbXkgdGFyZ2V0IHdlYiBzZXJ2ZXIgdGhhdCBtaWdodCBzZWUgbXkgY3Jhd2xpbmcgYm90IGFzIGEgRE5TIGF0dGFjay4NCiAgICAgIC54ICU+JQ0KICAgICAgICByZWFkX2h0bWwoKQ0KICAgIH0pLA0KICBzdW1tYXJ5X3VybCA9IG5hdl9kZiR1cmxbMTozXQ0KKQ0KDQpuYXZfcmVzdWx0c19saXN0DQpgYGANCg0KTm93IEkgaGF2ZSB0aHJlZSByb3dzIG9mIF9saXN0c18sIGVhY2ggbGlzdCB3aXRoIDUwIGxpbmtzLCBpbiBhIHRpYmJsZS4gIEVhY2ggbGluayBsZWFkcyB0byBhIHRhcmdldCBuYW1lIHRoYXQgSSBjYW4gZXZlbnR1YWxseSBgcmVhZF9odG1sYCB0byBnYXRoZXIgdGhlIHJhdyBIVE1MIG9mIHRoYXQgdGFyZ2V0IG5hbWUuIA0KDQpCdXQgZmlyc3QsIEkgd2FudCB0byBleHBhbmQgdGhlIHRocmVlIGxpc3RzIHNvIEkgaGF2ZSBhIHNpbmdsZSB0aWJibGUgb2YgMTUwIFVSTHMgdG8gdGFyZ2V0IG5hbWVzLiAgVXNpbmcgcHVycnIgKGBtYXAoKWApLCBJIGNhbiBpdGVyYXRlIG92ZXIgdGhlIHJlc3VsdHMgX2xpc3RzXywgcGFyc2luZyB0aGUgSFRNTCBub2RlcyB3aXRoIGBodG1sX2F0dHIoKWAgYW5kIGBodG1sX3RleHQoKWAuICBJdCBpcyBjb252ZW5pZW50IHRvIGtlZXAgdGhpcyBwYXJzZWQgZGF0YSBpbiBhIHRpYmJsZS4gIFRoZSByZXN1bHRzIHdpbGwgYmUgbmVzdGVkIGxpc3RzIHdpdGhpbiBhIHRpYmJsZS4gIFdoZW4gSSBleHBhbmQgdGhlIG5lc3RlZCBfbGlzdF8gd2l0aCB0aGUgYHVubmVzdCgpYCBmdW5jdGlvbiwgSSB0aGVuIGhhdmUgYSBzaW5nbGUgdGliYmxlIHdpdGggMTUwIFVSTHMgYW5kIDE1MCBuYW1lcywgb25lIHJvdyBmb3IgZWFjaCB0YXJnZXQgbmFtZS4gIA0KDQpgYGB7cn0NCnJlc3VsdHNfYnlfcGFnZSA8LSB0aWJibGUoc3VtbWFyeV91cmwgPSBuYXZfcmVzdWx0c19saXN0JHN1bW1hcnlfdXJsLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgdXJsID0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXAobmF2X3Jlc3VsdHNfbGlzdCRodG1sX3Jlc3VsdHMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH4gLnggJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaHRtbF9ub2RlcygidWwgbGkgYSIpICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGh0bWxfYXR0cigiaHJlZiIpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFwKG5hdl9yZXN1bHRzX2xpc3QkaHRtbF9yZXN1bHRzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB+IC54ICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGh0bWxfbm9kZXMoInVsIGxpIGEiKSAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBodG1sX3RleHQoKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApDQogICAgICAgICAgICAgICAgICAgICAgICAgICkNCg0KcmVzdWx0c19ieV9wYWdlDQoNCnJlc3VsdHNfYnlfcGFnZSAlPiUgDQogIHVubmVzdChjb2xzID0gYyh1cmwsIG5hbWUpKSAlPiUgDQogIGZpbHRlcighc3RyX2RldGVjdChuYW1lLCAiRUNBUlRJQ08iKSkgJT4lIA0KICBmaWx0ZXIoIXN0cl9kZXRlY3QobmFtZSwgIl5cXCsiKSkNCiAgDQpgYGANCg0KTm93IEkgY2FuIGl0ZXJhdGUgb3ZlciBlYWNoIG9uZSBvZiB0aGUgVVJMcyB0byB0aGUgdGFyZ2V0IG5hbWVzLiAgVGhlbiBJIGNhbiBwYXJzZSB0aGUgcmF3IEhUTUwgZm9yIGVhY2ggdGFyZ2V0IG5hbWUgcGFnZS4gV2hlbiBJIGZvbGxvdyB0aGUgbGlua3MgZm9yIGVhY2ggbmFtZSwgSSBoYXZlIHRoZSByYXcgSFRNTCBvZiBlYWNoIHBlcnNvbiwgaW4gX2xpc3RzXywgcmVhZHkgdG8gYmUgcGFyc2VkIHdpdGggdGhlIGBodG1sX25vZGVzYCwgYGh0bWxfdGV4dGAsIGFuZCBgaHRtbF9hdHRyYCBmdW5jdGlvbnMuDQoNCiMjIFBhcnNpbmcgZXhhbXBsZSBmb3IgYW4gaW5kaXZpZHVhbCANCg0KTm93IHlvdSBrbm93IGhvdyB0byBnZXQgYSBVUkwgZm9yIGVhY2ggbmFtZSBpbiB0aGUgdGFyZ2V0IGRhdGFiYXNlLiAgVGhhdCBpcywgeW91IGNhbiBjcmF3bCB0aGUgdGFyZ2V0IHNpdGUncyBuYXZpZ2F0aW9uLiAgVGhlIG5leHQgZ29hbCBpcyB0byBpbXBvcnQgYW5kIHBhcnNlIHRoZSBIVE1MIGZvciBlYWNoIF9uYW1lXy4gIEluIG90aGVyIHdvcmRzLCBpbiBteSBkZXZlbG9wbWVudCB0aWJibGUsIEkgc3RpbGwgbmVlZCB0byBjcmF3bCB0aGUgaW5kaXZpZHVhbCB0YXJnZXQgbmFtZXMsIGFsbCAxNTAgbmFtZXMsIDUwIG5hbWVzIHBlciBzdW1tYXJ5IHBhZ2UgZm9yIGVhY2ggb2YgdGhlIDMgZGV2ZWxvcG1lbnQgcGFnZXMuICBCZWxvdyBpcyBhbiBleGFtcGxlIG9mIGdhdGhlcmluZyBhbmQgcGFyc2luZyBpbmZvcm1hdGlvbiBmb3Igb25lIFVSTCByZXByZXNlbnRpbmcgb25lIHBlcnNvbi4gIFRoZSBpbmZvcm1hdGlvbiBnYXRoZXJlZCBpcyBpbmZvcm1hdGlvbiBmcm9tIHRoZSBkZXRhaWxlZCBuYW1lcyBwYWdlIGFib3V0IHRoZSBjaGlsZHJlbiBvZiBvbmUgcGVyc29uIGluIHRoZSB0YXJnZXQgZGF0YWJhc2UuDQoNCmBgYHtyfQ0KIyBodHRwOi8vd3d3LnZvbmRlbC5odW1hbml0aWVzLnV2YS5ubC9lY2FydGljby9wZXJzb25zLzEwNTc5DQojIHNjaGVtYTpjaGlsZHJlbg0KDQplbWFudWVsIDwtICByZWFkX2h0bWwoImh0dHA6Ly93d3cudm9uZGVsLmh1bWFuaXRpZXMudXZhLm5sL2VjYXJ0aWNvL3BlcnNvbnMvMTA1NzkiKQ0KDQpjaGlsZF9maWx0ZXIgPC0gZW1hbnVlbCAlPiUgDQogIGh0bWxfbm9kZXMoInVsIGxpIGEiKSAlPiUgDQogIGh0bWxfYXR0cigicmVsIikgJT4lIA0KICBhc190aWJibGUoKSAlPiUgDQogIG11dGF0ZShpZCA9IHJvd19udW1iZXIoKSkgJT4lIA0KICBmaWx0ZXIoc3RyX2RldGVjdCh2YWx1ZSwgImNoaWxkcmVuIikpDQpjaGlsZF9maWx0ZXINCg0KY2hpbGRfdGV4dCA8LSBlbWFudWVsICAlPiUgDQogIGh0bWxfbm9kZXMoInVsIGxpIGEiKSAlPiUgDQogIGh0bWxfdGV4dCgpICU+JSANCiAgYXNfdGliYmxlKCkgJT4lIA0KICByZW5hbWUodGV4dCA9IHZhbHVlKSAlPiUgDQogIG11dGF0ZShpZCA9IHJvd19udW1iZXIoKSkgJT4lIA0KICBpbm5lcl9qb2luKGNoaWxkX2ZpbHRlcikNCg0KY2hpbGRfdGV4dCAlPiUgDQogIHB1bGwodGV4dCkNCg0KDQpgYGANCg0KRG9uJ3QgZm9yZ2V0IHRvIHVzZSBhIHBhdXNlIGBTeXMuc2xlZXAoKWAgYmV0d2VlbiBlYWNoIHN5c3RlbWF0aWMgaXRlcmF0aW9uIG9mIHRoZSBgcmVhZF9odG1sKClgIGZ1bmN0aW9uLg0KDQojIyBSZXNvdXJjZXMNCg0KLSBodHRwczovL3J2ZXN0LnRpZHl2ZXJzZS5vcmcvDQotIGh0dHBzOi8vY29tbXVuaXR5LnJzdHVkaW8uY29tL3QvbG9vcC1mb3Itd2l0aC1ydmVzdC1mb3Itc2NyYXBpbmcvNTYxMzMvNCAobG9vcGluZyB3aXRoIFJWRVNUKQ0KLSBwdXJyciAvIG1hcCA6OiAgaHR0cHM6Ly9qZW5ueWJjLmdpdGh1Yi5pby9wdXJyci10dXRvcmlhbC9sczAxX21hcC1uYW1lLXBvc2l0aW9uLXNob3J0Y3V0cy5odG1sDQoNCi0tLQ0KPGNlbnRlcj4NCioqSm9obiBMaXR0bGUqKg0KDQoqKkRhdGEgU2NpZW5jZSBMaWJyYXJpYW4qKiAgDQpDZW50ZXIgZm9yIERhdGEgJiBWaXN1YWxpemF0aW9uIFNjaWVuY2VzICANCkR1a2UgVW5pdmVyc2l0eSBMaWJyYXJpZXMgIA0KDQpodHRwczovL0pvaG5MaXR0bGUuaW5mbyAgDQpodHRwczovL1JmdW4ubGlicmFyeS5kdWtlLmVkdSAgDQpodHRwczovL2xpYnJhcnkuZHVrZS5lZHUvZGF0YSAgDQoNCjxpIGNsYXNzPSJmYWIgZmEtY3JlYXRpdmUtY29tbW9ucyBmYS0yeCI+PC9pPiAmbmJzcDsgPGkgY2xhc3M9ImZhYiBmYS1jcmVhdGl2ZS1jb21tb25zLWJ5IGZhLTJ4Ij48L2k+PGkgY2xhc3M9ImZhYiBmYS1jcmVhdGl2ZS1jb21tb25zLW5jIGZhLTJ4Ij48L2k+ICANCkNyZWF0aXZlIENvbW1vbnM6ICBBdHRyaWJ1dGlvbi1Ob25Db21tZXJjaWFsIDQuMCAgDQpodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktbmMvNC4wDQo8L2NlbnRlcj4NCg0KJm5ic3A7ICA=